<?php
namespace yphp;

/*
 * 安全相关类
 */
class Safe extends Instance
{
    // 生成随机编码
    static function createCode()
    {
        return str_shuffle(Config::web('base_code'));
    }

    // 显示加密后的ajax的键值
    static function showAjax()
    {
        $ajax = explode('&',Config::web('ajax_param'));
        $key = Safe::en($ajax[0]);
        $val = isset($ajax[1]) ? Safe::en($ajax[1]) : null;
        return [$key,$val];
    }

    //+----------------------------------------------------------------------
    //  加密、解密
    //+----------------------------------------------------------------------

    static function encode($str)
    {
        if (mb_strlen($str) <= 1) return '';
        // 密锁串、长度、随机位及值
        $lock = Config::web('base_code');
        $len = strlen($lock);
        $rand = mt_rand(0, $len - 1);
        $lk = $lock[$rand];
        // 密钥结合密锁随机值MD5加密
        $md5 = strtoupper(md5(Config::web('safe_key') . $lk));
        // 字符串基础加密
        $str = self::en64($str);
        $res = '';
        for ($i = $k = 0, $c = strlen($str); $i < $c; $i++) {
            $k === strlen($md5) && $k = 0;
            // 转化字符：由密锁串 原位+随机位+顺序MD5密钥字符ASCII码 决定新位，从密锁串中获取目标字符
            $res .= $lock[(strpos($lock, $str[$i]) + $rand + ord($md5[$k])) % ($len)];
            $k++;
        }
        // 返回加密结果(含随机关联)
        return $res . $lk;
    }

    static function decode($str)
    {
        if (mb_strlen($str) <= 1) return '';
        // 密锁串、长度、随机位及值
        $lock = Config::web('base_code');
        $len = strlen($lock);
        // 字符串长度
        $txtLen = strlen($str);
        // 密锁随机值及位
        $lk = $str[$txtLen - 1];
        $rand = strpos($lock, $lk);
        // 密钥结合密锁随机值MD5加密
        $md5 = strtoupper(md5(Config::web('safe_key') . $lk));
        // 去除字符串随机关联
        $str = substr($str, 0, $txtLen - 1);
        $tmpStream = '';
        for ($i = $k = 0, $c = strlen($str); $i < $c; $i++) {
            $k === strlen($md5) && $k = 0;
            // 获取字符在密锁串原位：由 位-随机位-顺序MD5密钥字符ASCII码 算出
            $j = strpos($lock, $str[$i]) - $rand - ord($md5[$k]);
            while ($j < 0) {
                $j += $len;
            }
            $tmpStream .= $lock[$j];
            $k++;
        }
        // 返回基础解密源字符串
        return self::de64($tmpStream);
    }

    //+----------------------------------------------------------------------
    //  基础加密、基础解密
    //+----------------------------------------------------------------------

    static function en($str)
    {
        // 加密条件：非空字符串
        if (is_string($str) === false || strlen($str) === 0) return $str;
        // 不重复密锁串
        $lock = Config::web('base_code');
        $len = strlen($lock);
        // 字符串无符号解包数组值数组
        $bytes = array_values(unpack('C*', $str));
        // 数组元素256倍升幂
        $num256 = $bytes[0];
        for ($i = 1, $l = count($bytes); $i < $l; $i++) {
            // 大数字操作：和、积
            $num256 = bcadd(bcmul($num256, 256), $bytes[$i]);
        }
        $res = '';
        // 密锁长度倍降幂字符串
        while ($num256 >= $len) {
            // 大数字操作：取余
            $res = $lock[bcmod($num256, $len)] . $res;
            // 大数字操作：商(保留0位小数)
            $num256 = bcdiv($num256, $len, 0);
        }
        $res = $lock[$num256] . $res;
        // 数组前0元素补充
        foreach ($bytes as $byte) {
            if ($byte !== 0) break;
            $res = $lock[0] . $res;
        }
        // $lock[0]... $lock[...]...
        return (string)$res;
    }

    static function de($str)
    {
        // 解密条件：非空字符串
        if (is_string($str) === false || strlen($str) === 0) return $str;
        // 密锁串分割数组并交换键值
        $lock = array_flip(str_split(Config::web('safe_key')));
        $len = count($lock);
        // 字符串分割数组
        $chars = str_split($str);
        // 验证加密字符串合法性
        foreach ($chars as $char) {
            if (!key_exists($char, $lock)) return false;
        }
        // 字符组元素密钥长度升幂
        $num256 = $lock[$chars[0]];
        for ($i = 1, $l = count($chars); $i < $l; $i++) {
            // 大数字操作：和、积
            $num256 = bcadd(bcmul($num256, $len), $lock[$chars[$i]]);
        }
        $res = '';
        // 256倍降幂还原字符串
        while ($num256 > 0) {
            $res = pack('C', bcmod($num256, 256)) . $res;
            $num256 = bcdiv($num256, 256, 0);
        }
        // 字符组还原前0数还原ASCII码
        foreach ($chars as $char) {
            if ($lock[$char] === 0) {
                $res = "\x00" . $res;
                continue;
            }
            break;
        }
        return $res;
    }

    // ----------------------------------------------------------------------
    //  BASE64加解密
    // ----------------------------------------------------------------------

    static function en64($str)
    {
        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($str));
    }

    static function de64($str)
    {
        return base64_decode(str_replace(['-', '_'], ['+', '/'], $str));
    }


    //+----------------------------------------------------------------------
    //  CSRF 令牌
    //+----------------------------------------------------------------------

    static function csrf_token()
    {
        $str = self::createCode();
        $code = substr(self::en($str),0,40);
        Session::set('csrf_token',$code);
        return $code;
    }

}